/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.utils;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.internna.iwebmvc.utils.CollectionUtils;
import static org.internna.iwebmvc.utils.StringUtils.hasText;


/**
 * Utilities to read a Maven pom.xml and retrieve data.
 *
 * @author Jose Noheda
 * @since 1.0
 */
public final class MavenUtils {

    private static final Map<String, String> mavenNamespace = new HashMap<String, String>(2);

    static {
        mavenNamespace.put("xs", "http://www.w3.org/2001/XMLSchema");
        mavenNamespace.put("mvn", "http://maven.apache.org/POM/4.0.0");
    }

    /**
     * Valid types of Maven modules (pom, jar, maven-plugin, war)
     */
    public enum Packaging {

        POM("pom"), JAR("jar"), MAVEN_PLUGIN("maven-plugin"), WAR("war");

        private String value;

        private Packaging(String value) {
            this.value = value;
        }

        @Override public String toString() {
            return value;
        }

        public static Packaging fromValue(String value) {
            if ("pom".equals(value))  {
                return Packaging.POM;
            }
            if ("war".equals(value)) {
                return Packaging.WAR;
            }
            if ("maven-plugin".equals(value)) {
                return Packaging.MAVEN_PLUGIN;
            }
            return Packaging.JAR;
        }

    }

    /**
     * Default private constructor for a Utility class.
     */
    private MavenUtils() {
        throw new AssertionError("Do not try to instantiate utility class");
    }

    /**
     * Returns any (nested) node from a maven document.
     *
     * @param pomNode any
     * @param path any
     * @return the content of a (nested) tag as a node
     */
    public static Node getMavenNode(Node pomNode, String path) {
        Node found = null;
        if ((pomNode != null) && (hasText(path))) {
            XPath xpath = pomNode.createXPath(path);
            xpath.setNamespaceURIs(mavenNamespace);
            found = xpath.selectSingleNode(pomNode);
        }
        return found;
    }

    /**
     * Returns a collection of nodes that match the provided path scoped by the provided node.
     *
     * @param pomNode
     * @param path
     * @return
     */
    public static Collection<Node> getMavenNodes(Node pomNode, String path) {
        Collection<Node> found = null;
        if ((pomNode != null) && (hasText(path))) {
            XPath xpath = pomNode.createXPath(path);
            xpath.setNamespaceURIs(mavenNamespace);
            found = xpath.selectNodes(pomNode);
        }
        return found;
    }

    /**
     * Convenience method to get all the modules in a POM.
     *
     * @param pom any
     * @return a not null collection containing all the textual content of all the module tags
     */
    public static Set<String> getModuleNames(Node pom) {
        Collection<Node> modules = getMavenNodes(getMavenNode(pom, "/mvn:project/mvn:modules"), "mvn:module");
        Set<String> names = Collections.EMPTY_SET;
        if (CollectionUtils.isNotEmpty(modules)) {
            names = new HashSet<String>(modules.size());
            for (Node module : modules) {
                names.add(module.getText());
            }
            return names;
        }
        return names;
    }

    /**
     * Returns the textual content of the description tag in a Maven pom.xml file.
     *
     * @param pom any
     * @return a string
     */
    public static String getDescription(Node pom) {
        return getTopNodeContent(pom, "description");
    }

    /**
     * Returns the textual content of the url tag in a Maven pom.xml file.
     *
     * @param pom any
     * @return a string
     */
    public static String getUrl(Node pom) {
        return getTopNodeContent(pom, "url");
    }

    /**
     * Returns the textual content of the name tag in a Maven pom.xml file.
     *
     * @param pom any
     * @return a string
     */
    public static String getName(Node pom) {
        return getTopNodeContent(pom, "name");
    }

    /**
     * Same as getVersion(pom, true)
     *
     * @param pom any
     * @return a string
     */
    public static String getVersion(Node pom) {
        return getVersion(pom, true);
    }

    /**
     * Gets the version pom attribute.
     *
     * @param pom any
     * @param removeSuffix if true the SNAPSHOT suffix will be removed from the response
     * @return any string
     */
    public static String getVersion(Node pom, boolean removeSuffix) {
        String version = getTopNodeContent(pom, "version");
        return removeSuffix && hasText(version) && (version.indexOf("-SNAPSHOT") > 0) ? version.replace("-SNAPSHOT", "") : version;
    }

    /**
     * Returns the textual content of the packaging tag in a Maven pom.xml file.
     *
     * @param pom any
     * @return JAR unless the pom specifies one
     */
    public static Packaging getPackaging(Node pom) {
        String packaging = getTopNodeContent(pom, "packaging");
        return hasText(packaging) ? Packaging.fromValue(packaging.toLowerCase(Locale.ENGLISH)) : Packaging.JAR;
    }

    private static String getTopNodeContent(Node pom, String path) {
        Node desc = pom == null ? null : getMavenNode(pom, "/mvn:project/mvn:" + path);
        return desc != null ? parse(desc.getText()) : null;
    }

    /**
     * Removes spaces and line feeds that will cause problems later.
     *
     * @param input any
     * @return the input string without extra spaces in one line
     */
    protected static String parse(String input) {
        String parsed = input;
        if (parsed != null) {
            parsed = parsed.replaceAll("\\n", " ").replaceAll("\\t", " ").replaceAll("\\s{2,}", " ");
            parsed = parsed.trim();
            if (!hasText(parsed)) {
                parsed = null;
            }
        }
        return parsed;
    }

}
